Skip to content

[jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes and rename "useQuarkusSecurityAnnotations" to "useJakartaSecurityAnnotations"#23680

Open
Ignacio-Vidal wants to merge 13 commits intoOpenAPITools:masterfrom
Ignacio-Vidal:quarkus-authentication

Conversation

@Ignacio-Vidal
Copy link
Copy Markdown
Contributor

@Ignacio-Vidal Ignacio-Vidal commented May 3, 2026

This is part 2 of #23691 to improve authentication and authorisation support in the `jaxrs-spec/quarkus` server generator.

What this PR does

When the new useJakartaSecurityAnnotations flag is enabled (Quarkus library only), the generator emits @jakarta.annotation.security.RolesAllowed({"**"}) on JAX-RS interface methods and implementation stubs for operations whose security requirements mean any authenticated user is sufficient — i.e. no specific role or scope is enforced by the token.

Schemes that qualify

Scheme type Condition
HTTP Basic / Bearer Always — no scope concept
API Key Always — no scope concept
MutualTLS Always — no scope concept
OAuth2 / OpenID Connect Only when the operation's scope list is empty (scheme: [])

OR semantics — at least one alternative qualifies

The OpenAPI security array is OR: a request is authorised if any one of the listed alternatives is satisfied. The annotation is emitted when at least one OR alternative fully qualifies on its own:

security:
  - oauth2_read: [read:items]   # scoped — does not qualify alone
  - oauth2_admin: []            # empty scopes — qualifies → annotation emitted

AND semantics — all co-required schemes must qualify

A single SecurityRequirement object with multiple keys is AND: all listed schemes must be satisfied simultaneously. If any scheme in the AND group carries explicit scopes, the combined requirement is more restrictive than "any authenticated user", so the annotation is not emitted:

security:
  - oauth2Scheme: []            # would qualify alone…
    openIdScheme: [admin:create] # …but AND'd with a scoped scheme → not emitted

Why the annotation is not emitted when scopes are present

@RolesAllowed({"**"}) means any authenticated principal. If scopes are present, the intent is to restrict to principals holding a specific role. Emitting @RolesAllowed({"**"}) in that case would be too permissive and contradict the spec. Those operations are left unannotated pending a follow-up PR that will emit @RolesAllowed({scope}) for the all-scoped case.

Emitting both annotations on the same method is not a valid fallback: Quarkus applies them with AND semantics, making @RolesAllowed({"**"}) redundant at best and silently incorrect at worst.

Implementation notes

Why fromOperation and not postProcessOperationsWithModels

The OpenAPI security array uses a List<SecurityRequirement> where each element is a map of scheme name → scope list. A single map entry with multiple keys is an AND group. By the time postProcessOperationsWithModels runs, DefaultGenerator.filterAuthMethods has already flattened all SecurityRequirement objects into a plain List<CodegenSecurity>, discarding which schemes were co-located in the same AND group. Evaluating mixed-scope AND groups correctly requires the raw structure, which is still available in fromOperation via the Operation parameter. The inherited protected OpenAPI openAPI field provides access to the security scheme definitions needed to resolve scheme types by name.

Vendor extension

fromOperation sets x-jakarta-any-roles = true on qualifying operations. The Mustache templates for both apiInterface.mustache (interface-only mode) and apiMethod.mustache (implementation stub mode) emit the annotation inside a {{#vendorExtensions.x-jakarta-any-roles}} block.

Migration

Replace useQuarkusSecurityAnnotations with useJakartaSecurityAnnotations so the support for authentication and authorisation annotations can be extended to other libraries under the same generator. #23699 added the initial useQuarkusSecurityAnnotations annotation to master and has not been released in a tag version yet

Generated code now uses @jakarta.annotation.security.RolesAllowed({"**"}) instead of @io.quarkus.security.Authenticated.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for OAuth2 security schemes without scopes DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for OAuth2 security schemes with empty scopes array May 4, 2026
@Ignacio-Vidal Ignacio-Vidal force-pushed the quarkus-authentication branch 3 times, most recently from 7479247 to 5daad84 Compare May 5, 2026 21:31
@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for OAuth2 security schemes with empty scopes array DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes array May 5, 2026
@Ignacio-Vidal Ignacio-Vidal force-pushed the quarkus-authentication branch from 5daad84 to 9b2aa6e Compare May 6, 2026 22:12
@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes array DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes May 6, 2026
@Ignacio-Vidal Ignacio-Vidal marked this pull request as ready for review May 7, 2026 07:07
@Ignacio-Vidal Ignacio-Vidal marked this pull request as draft May 7, 2026 07:08
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="quarkus-security-github-issue.md">

<violation number="1" location="quarkus-security-github-issue.md:59">
P1: OpenAPI scope requirements are conjunctive, but this mapping uses `@RolesAllowed` OR semantics and can under-enforce multi-scope authorization.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

| `apiKey` | `@Authenticated` | An API key validates identity only. No role check is applicable. |
| `oauth2` with empty scopes (`[]`) | `@Authenticated` | An empty scope list means "any authenticated user" — no specific authorization is required beyond a valid token. |
| `openIdConnect` with empty scopes (`[]`) | `@Authenticated` | Same reasoning as OAuth2 with empty scopes. |
| `oauth2` with explicit scopes | `@RolesAllowed({"scope1", "scope2"})` | In Quarkus, OAuth2/OIDC token scopes are mapped to `SecurityIdentity` roles. `@RolesAllowed` receives the scopes as role names. |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: OpenAPI scope requirements are conjunctive, but this mapping uses @RolesAllowed OR semantics and can under-enforce multi-scope authorization.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At quarkus-security-github-issue.md, line 59:

<comment>OpenAPI scope requirements are conjunctive, but this mapping uses `@RolesAllowed` OR semantics and can under-enforce multi-scope authorization.</comment>

<file context>
@@ -0,0 +1,209 @@
+| `apiKey` | `@Authenticated` | An API key validates identity only. No role check is applicable. |
+| `oauth2` with empty scopes (`[]`) | `@Authenticated` | An empty scope list means "any authenticated user" — no specific authorization is required beyond a valid token. |
+| `openIdConnect` with empty scopes (`[]`) | `@Authenticated` | Same reasoning as OAuth2 with empty scopes. |
+| `oauth2` with explicit scopes | `@RolesAllowed({"scope1", "scope2"})` | In Quarkus, OAuth2/OIDC token scopes are mapped to `SecurityIdentity` roles. `@RolesAllowed` receives the scopes as role names. |
+| `openIdConnect` with explicit scopes | `@RolesAllowed({"scope1"})` | Same as OAuth2 with scopes. |
+| OR list with at least one empty-scope scheme | `@Authenticated` | The least restrictive alternative dominates: if any scheme allows any authenticated user, the effective gate is authentication only. |
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the generator handles joint and disjoint sets of security schemes added in the openapi specification

@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @Authenticated for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({**})d for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes May 8, 2026
@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({**})d for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({**}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes May 8, 2026
@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({**}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes May 8, 2026
@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes and rename "useQuarkusSecurityAnnotations" to "useJakartaSecurityAnnotations" May 8, 2026
@Ignacio-Vidal Ignacio-Vidal force-pushed the quarkus-authentication branch from e92ab2e to b3b43db Compare May 8, 2026 21:30
@Ignacio-Vidal Ignacio-Vidal marked this pull request as ready for review May 8, 2026 23:40
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 20 files

@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes and rename "useQuarkusSecurityAnnotations" to "useJakartaSecurityAnnotations" [jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes and rename "useQuarkusSecurityAnnotations" to "useJakartaSecurityAnnotations" May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant